package com.echo.code.arithmetic.chapter10;

import static java.lang.StrictMath.pow;
import static java.lang.StrictMath.sqrt;

import com.echo.code.arithmetic.chapter07.Optional;
import java.util.LinkedList;
import java.util.List;

/**
 * Created by baiyap on 2018/8/16.
 */
public class KNN {

  public static void main(String[] args) {
    List<BreadNumber> breadNumbers = new LinkedList<BreadNumber>() {{
      add(new BreadNumber(5, 1, 0, 300));
      add(new BreadNumber(3, 1, 1, 225));
      add(new BreadNumber(1, 1, 0, 75));
      add(new BreadNumber(4, 0, 0, 200));
      add(new BreadNumber(4, 0, 0, 150));
      add(new BreadNumber(2, 0, 0, 50));
    }};
    BreadNumber today = new BreadNumber(4, 1, 0, 0);
    double prediction = knn(breadNumbers, today, 4);
    System.out.println(prediction);
  }

  private static double knn(List<BreadNumber> breadNumbers, BreadNumber today, int k) {
    breadNumbers.forEach(breadNumber -> breadNumber.distance = computeDistance(breadNumber, today));

    // 寻找最近的k个邻居
    List<BreadNumber> neighbors = new LinkedList<>();
    while (k > 0 && !breadNumbers.isEmpty()) {
      Optional<BreadNumber> nearestNeighbor = Optional.getInstance();
      Optional<Double> nearest = Optional.getInstance(Double.MAX_VALUE);
      breadNumbers.forEach(breadNumber -> {
        if (breadNumber.distance < nearest.getDouble()) {
          nearest.setValue(breadNumber.distance);
          nearestNeighbor.setValue(breadNumber);
        }
      });
      k--;
      neighbors.add(nearestNeighbor.getValue());
      breadNumbers.remove(nearestNeighbor.getValue());
    }
    // 预测today的销量
    double todayNumber = neighbors.stream().mapToDouble(breadNumber -> breadNumber.number)
        .reduce(0.0, (acc, element) -> acc + element);
    return todayNumber / neighbors.size();
  }

  /**
   * 计算距离.
   */
  private static double computeDistance(BreadNumber breadNumber, BreadNumber today) {
    return sqrt(pow((breadNumber.weather - today.weather), 2) +
        pow((breadNumber.isHoliday - today.isHoliday), 2) +
        pow((breadNumber.isActivity - today.isActivity), 2));
  }

  private static class BreadNumber {

    private int weather;
    private int isHoliday;
    private int isActivity;
    private int number;
    private double distance;

    public BreadNumber(int weather, int isHoliday, int isActivity, int number) {
      this.weather = weather;
      this.isHoliday = isHoliday;
      this.isActivity = isActivity;
      this.number = number;
    }
  }
}
